package cn.android.support.v7.lib.sin.crown.kotlin.socket

import android.app.Activity
import cn.android.support.v7.lib.sin.crown.utils.HttpUtils
import cn.android.support.v7.lib.sin.crown.utils.LoggerUtils
import kotlinx.coroutines.experimental.async
import java.net.InetAddress
import java.net.ServerSocket
import java.net.Socket

/**
 * 理论上IP的范围是0.0.0.0~255.255.255.255
 * 端口号为1~65535
 * 服务器端（本地服务器，必须确保客户端和服务器在同一局域网中才能通信）
 */
class TcpServerSocket(var serverIp: String = HttpUtils.getInstance().hostIP4, var serverPort: Int = Port) {

    companion object {
        var Ip = "0.0.0.0"//默认IP,0.0.0.0(即本机。运行在同一个机器上才有效)，如果ip显示的是 :	:: (这个就是0.0.0.0本机)
        var Port = 31052//默认端口号
        var backlog: Int = 200//backlog客户端连接请求的队列长度

        interface OpenServerSocketCallBack {
            //开始开启服务
            fun onStart(ip: String, port: Int, ipPort: String = ip + ":" + port)

            //服务开启成功
            fun onSuccess(it: TcpServerSocket)

            //服务开启失败,reason是失败的原因
            fun onFail(ip: String, port: Int, ipPort: String = ip + ":" + port, reason: String)

            //服务器已经开启，不会再执行以上方法
            fun onAlready(it: TcpServerSocket)

            //用户每链接一个，都会回调（即每链接一个新用户都会回调）
            fun onCustomer(it: TcpClientSocket)
        }
    }

    //端口号的范围从0到65535，比如用于浏览网页服务的80端口，用于FTP服务的21端口等等
    //知名端口即众所周知的端口号，范围从0到1023，这些端口号一般固定分配给一些服务
    //动态端口的范围从1024到65535，这些端口号一般不固定分配给某个服务，也就是说许多服务都可以使用这些端口。

    //端口号规定为16位，即允许一个IP主机有2的16次方65535个不同的端口
    //0~1023：分配给系统的端口号,如果设置端口为0，则系统会自动为其分配一个端口；
    //1024~49151：登记端口号，主要是让第三方应用使用
    //49152~65535：短暂端口号，是留给客户进程选择暂时使用，一个进程使用完就可以供其他进程使用。
    init {
        if (serverPort < 1024 || serverPort > 65535) {
            serverPort = Port
        }
    }

    var serverSocket: ServerSocket? = null
    //开启服务，回调链接的客服端。
    //activity不为空，则在主线程中回调。
    fun open(activity: Activity?=null, callbak: OpenServerSocketCallBack?=null): TcpServerSocket? {
        //fixme 服务已经开启
        serverSocket?.let {
            if (!(serverSocket?.isClosed ?: true)) {
                activity?.runOnUiThread {
                    callbak?.onAlready(this@TcpServerSocket)
                } ?: callbak?.onAlready(this@TcpServerSocket)
                return this
            }
        }
        async {
            try {
                //fixme 开始启动服务
                activity?.runOnUiThread {
                    callbak?.onStart(serverIp, serverPort)
                } ?: callbak?.onStart(serverIp, serverPort)
                //没有指定本地其他Ip的时候,默认ip就是 0.0.0.0(即本机。运行在同一个机器上才有效)，如果ip显示的是 :	:: (这个就是0.0.0.0本机)
                //serverSocket = ServerSocket(port)//注意，传入端口号
                //fixme 必须手动设置一个ip地址。这样才能通信。默认的ip，不行。InetAddress.getLocalHost()也不行。
                //fixme 局域网ip可以，外网ip不行
                //fixme 局域网，即同一个网段下，即IP地址的前三段必须相同。如：192.168.1.110，即192.168.1 前三段相同，就属于同一个网段。
                serverSocket = ServerSocket(serverPort, backlog, InetAddress.getByName(serverIp))

                //fixme 服务开启成功
                serverSocket?.let {
                    if (!(serverSocket?.isClosed ?: true)) {
                        activity?.runOnUiThread {
                            callbak?.onSuccess(this@TcpServerSocket)
                        } ?: callbak?.onSuccess(this@TcpServerSocket)
                    }

                    while (!serverSocket!!.isClosed) {
                        //var msg = "无限循环等待下一个客服链接中。。。服务器地址:\t" + getIp() + "\t端口号:\t" + getPort()+"\t外网:\t"+HttpUtils.getInstance().netIp
                        //LoggerUtils.e("test", msg)
                        //System.out.println(msg) //android 机器上，也能在控制台打印出来。
                        var socket: Socket? = serverSocket?.accept()//等待用户链接，阻塞线程
                        socket?.let {
                            var mySocket = TcpClientSocket(socket)
                            //msg = "客服端已链接，ip地址\t" + socket.inetAddress.hostAddress + "\t端口port：\t" + socket.port
                            //LoggerUtils.e("test", msg)
                            //System.out.println(msg)
                            async {
                                if (!socket.isClosed) {
                                    //fixme 新用户链接上
                                    activity?.runOnUiThread {
                                        callbak?.onCustomer(mySocket)
                                    } ?: callbak?.onCustomer(mySocket)
                                }
                            }
                        }
                    }
                }

            } catch (e: Exception) {
                //fixme 服务器，启动失败
                activity?.runOnUiThread {
                    callbak?.onFail(serverIp, serverPort, reason = e.message.toString())
                }
                        ?: callbak?.onFail(serverIp, serverPort, reason = e.message.toString())
                LoggerUtils.e("test", "服务开启异常:\t" + e.message)
            }
        }
        return this
    }

    //ServerSocket的isBound()方法判断ServerSocket是否已经与一个端口绑定，只要ServerSocket已经与一个端口绑定，即使它已经被关闭，isBound()方法也会返回true。
    //ServerSocket的isClosed()方法判断ServerSocket是否关闭，只有执行了ServerSocket的close()方法，isClosed()方法才返回true；否则，即使ServerSocket还没有和特定端口绑定，isClosed()方法也会返回false。
    //关闭服务，使服务器释放占用的端口，并且断开与所有客户的连接
    fun close(): TcpServerSocket {
        serverSocket?.let {
            if (!serverSocket!!.isClosed) {
                serverSocket?.close()//只有关闭了，isClosed才会返回true,否则一直返回false
            }
        }
        return this
    }

    //获取服务器ip地址
    fun getIp(): String? {
        return serverSocket?.inetAddress?.hostAddress
    }

    //获取端口号
    fun getPort(): Int? {
        return serverSocket?.localPort
    }

    //获取ip地址+端口号port 形式为 192.168.0.1:8889
    fun getIpPort(): String? {
        return getIp() + ":" + getPort()
    }

}